<?php
/**
 * | 节程 [ 节程赋能开发者，助力企业发展 ]
 * +----------------------------------------------------------------------
 *  | Copyright (c) 2020~2029 温州惊蛰网络科技有限公司 All rights reserved.
 * +----------------------------------------------------------------------
 *  | Licensed 节程并不是自由软件，未经许可不能去掉节程相关版权
 * +----------------------------------------------------------------------
 */


namespace app\utils;


class Weixin1
{
    private $token;
    private $encodingAesKey;
    private $appId;
    private $key;

    public function __construct($token='',$encodingAesKey='',$appId='')
    {

        $this->token = $token;
        $this->encodingAesKey = $encodingAesKey;
        $this->appId = $appId;
        $this->key = base64_decode($this->encodingAesKey . '=');
    }

    //xml格式化
    private function xmlFormat($xml)
    {
        $xmlTree = new \DOMDocument();
        $xmlTree->loadXML($xml);
        $encrypt = trim($xmlTree->getElementsByTagName('Encrypt')->item(0)->nodeValue);
        $format = "<xml><AppId><![CDATA[toUser]]></AppId><Encrypt><![CDATA[%s]]></Encrypt></xml>";
        return sprintf($format, $encrypt);
    }

    //xml提取
    private function xmlExtract($xmlText)
    {
        try {
            $xml = new \DOMDocument();
            $xml->loadXML($xmlText);
            return trim($xml->getElementsByTagName('Encrypt')->item(0)->nodeValue);
        } catch (\Exception $e) {
            return false;
        }
    }

    //xml生成
    private function xmlGenerate($encrypt, $signature, $timestamp, $nonce)
    {
        $format = "<xml><Encrypt><![CDATA[%s]]></Encrypt><MsgSignature><![CDATA[%s]]></MsgSignature><TimeStamp>%s</TimeStamp><Nonce><![CDATA[%s]]></Nonce></xml>";
        return sprintf($format, $encrypt, $signature, $timestamp, $nonce);
    }

    //获取随机16位字符串
    private function getRandomStr()
    {
        $str = "";
        $strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
        $max = strlen($strPol) - 1;
        for ($i = 0; $i < 16; $i++) {
            $str .= $strPol[mt_rand(0, $max)];
        }
        return $str;
    }

    //SHA1算法
    private function getSHA1($token, $timestamp, $nonce, $encryptMsg)
    {
        try {
            $array = array($encryptMsg, $token, $timestamp, $nonce);
            sort($array, SORT_STRING);

            $str = implode($array);

            return sha1($str);
        } catch (\Exception $e) {
            return false;
        }
    }

    //解密文本
    private function decodeText($text)
    {
        $pad = ord(substr($text, -1));
        if ($pad < 1 || $pad > 32) {
            $pad = 0;
        }
        return substr($text, 0, (strlen($text) - $pad));
    }

    //加密文本
    private function encodeText($text)
    {
        $blockSize = 32;
        $textLength = strlen($text);
        //计算需要填充的位数
        $amountToPad = $blockSize - ($textLength % $blockSize);
        if ($amountToPad == 0) {
            $amountToPad = $blockSize;
        }
        //获得补位所用的字符
        $padChr = chr($amountToPad);
        $tmp = "";
        for ($index = 0; $index < $amountToPad; $index++) {
            $tmp .= $padChr;
        }
        return $text . $tmp;
    }

    //解密字符串
    private function decryptString($encrypted, $appId)
    {
        try {
            //使用BASE64对需要解密的字符串进行解码
            $ciphertextDec = base64_decode($encrypted);
            $iv = substr($this->key, 0, 16);
            $decrypted = openssl_decrypt($ciphertextDec, 'AES-256-CBC', $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
        } catch (\Exception $e) {
            return false;
        }
        try {
            //去除补位字符
            $result = $this->decodeText($decrypted);
            //去除16位随机字符串,网络字节序和AppId
            if (strlen($result) < 16)
                return '';
            $content = substr($result, 16, strlen($result));
            $lenList = unpack('N', substr($content, 0, 4));
            $xmlLen = $lenList[1];
            $xmlContent = substr($content, 4, $xmlLen);
            $fromAppId = substr($content, $xmlLen + 4);
        } catch (\Exception $e) {
            return false;
        }
        if ($fromAppId != $appId)
            return false;
        return $xmlContent;
    }

    //加密字符串
    public function encryptString($text, $appid)
    {
        try {
            //获得16位随机字符串，填充到明文之前
            $random = $this->getRandomStr();
            $text = $random . pack("N", strlen($text)) . $text . $appid;
            // 网络字节序
            $iv = substr($this->key, 0, 16);
            //使用自定义的填充方式对明文进行补位填充
            $text = $this->encodeText($text);
            $encrypted = openssl_encrypt($text, 'AES-256-CBC', $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
            //使用BASE64对加密后的字符串进行编码
            return base64_encode($encrypted);
        } catch (\Exception $e) {
            return false;
        }
    }

    //解密消息
    private function decryptMsg($msgSignature, $timestamp = null, $nonce, $postData)
    {
        if (strlen($this->encodingAesKey) != 43) {
            return false;
        }
        //提取密文
        $encrypt = $this->xmlExtract($postData);

        if (!$encrypt) {
            return false;
        }
        if ($timestamp == null) {
            $timestamp = time();
        }

        //验证安全签名
        $signature = $this->getSHA1($this->token, $timestamp, $nonce, $encrypt);

        if (!$signature) {
            return false;
        }


        if ($signature != $msgSignature) {
            return false;
        }

        return $this->decryptString($encrypt, $this->appId);
    }

    //加密消息
    private function encryptMsg($replyMsg, $timeStamp, $nonce)
    {
        //加密
        $encrypt = $this->encryptString($replyMsg, $this->appId);
        if (!$encrypt) {
            return false;
        }
        if ($timeStamp == null) {
            $timeStamp = time();
        }
        //生成安全签名
        $signature = $this->getSHA1($this->token, $timeStamp, $nonce, $encrypt);
        if (!$signature) {
            return false;
        }
        //生成发送的xml
        return $this->xmlGenerate($encrypt, $signature, $timeStamp, $nonce);
    }

    //解密消息示例
    public function aesDecode($msg_sign,$timeStamp,$nonce,$encryptMsg)
    {

        $fromXml = $this->xmlFormat($encryptMsg);

        return $this->decryptMsg($msg_sign, $timeStamp, $nonce, $fromXml);
    }

    //加密消息示例
    public function aesEncode()
    {
        $timeStamp = '1626140909';
        $nonce = '987390923';
        $text = '<xml>
	<AppId>
		<![CDATA[third_app_id]]>
	</AppId>
	<CreateTime>
	    1626140909
	</CreateTime>
	<InfoType>
		<![CDATA[component_verify_ticket]]>
	</InfoType>
	<ComponentVerifyTicket>
		<![CDATA[ticket@@@ticket]]>
	</ComponentVerifyTicket>
</xml>';
        return $this->encryptMsg($text, $timeStamp, $nonce);
    }
}
